              package com.java.diagnostics.visualizer.parser.javadump;
              
              import com.java.diagnostics.visualizer.data.DataLevel;
              import com.java.diagnostics.visualizer.data.SourceData;
              import com.java.diagnostics.visualizer.data.StructuredDataBuilder;
              import com.java.diagnostics.visualizer.data.UnstructuredData;
              import com.java.diagnostics.visualizer.exceptions.GCAndMemoryVisualizerException;
              import com.java.diagnostics.visualizer.factory.DataFactory;
              import com.java.diagnostics.visualizer.gc.defaultextensions.util.Messages;
              import com.java.diagnostics.visualizer.impl.data.SourceDataImpl;
              import com.java.diagnostics.visualizer.parser.vgc.VGCDataManager;
              import com.java.diagnostics.visualizer.parser.vgc.VGCPlainTextParser;
              import com.java.diagnostics.visualizer.properties.OutputProperties;
              import com.java.diagnostics.visualizer.sources.Source;

              import java.io.BufferedReader;
              import java.io.IOException;
              import java.text.ParseException;
              import java.text.SimpleDateFormat;
              import java.util.Collections;
              import java.util.Comparator;
              import java.util.Date;
              import java.util.HashMap;
              import java.util.HashSet;
              import java.util.LinkedList;
              import java.util.List;
              import java.util.Set;
              import java.util.regex.Matcher;
              import java.util.regex.Pattern;
              
              public class InstanceJavaDumpParser extends VGCPlainTextParser
              {
                private static final String IBM = "IBM";
                public static final String VERBOSEGCLOG = "VERBOSEGCLOG";
                public static final String VERBOSEGCENABLED = "VERBOSEGCENABLED";
                public static final String FILETYPE = "FILETYPE";
                private static final String LOCALGC_EVENT = "LocalGC";
                private static final String GLOBALGC_EVENT = "GlobalGC";
                private HashMap<String, String> allFields = new HashMap();
                private long baseTimeStamp;
                private static final Set<String> GCCYCLEEVENTS = new HashSet();
                private static final String TWODIGITS = "[0-9][0-9]";
                private static final String SECONDFRACTIONDIGITS = "[0-9][0-9][0-9]*";
                private static final String TIMEZONE = "[A-Z][A-Z][A-Z]";
                private static final String STRINGPATTERN = "[A-Za-z0-9]+";
                private static final String openBracket = "(";
                private static final String closeBracket = ")";
                private static final String COLON = ":";
                private static final String SPACE = " ";
                private static final String EVENTSTARTPATTERN = "3STHSTTYPE     ([0-9][0-9]:[0-9][0-9]:[0-9][0-9]:[0-9][0-9][0-9]* [A-Z][A-Z][A-Z]) j9mm.[0-9][0-9] -   ([A-Za-z0-9]+) start:? (.*)";
                private static final String EVENTENDPATTERN = "3STHSTTYPE     ([0-9][0-9]:[0-9][0-9]:[0-9][0-9]:[0-9][0-9][0-9]* [A-Z][A-Z][A-Z]) j9mm.[0-9][0-9] -   ([A-Za-z0-9]+) end:? (.*)";
                private static final String CORETIMESTAMPPATTERN = "1TIDATETIME.*Date:.* ([0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]) at [0-9][0-9]:[0-9][0-9]:[0-9][0-9]";
                private static final String VERBOSEGCFLAGPATTERN = "2CIUSERARG *-verbose:gc";
                private static final String VERBOSEGCLOGPATTERN = "2CIUSERARG *-Xverbosegclog:(.*)";
                private static final int DATEGROUP = 1;
                private static final int EVENTTYPEGROUP = 2;
                private static final int EXTRADATAGROUP = 3;
                private boolean verboseGCEnabled = false;
                private String verboseGCLogName = null;
              
                private final DataFactory hiddenFactory = DataFactory.getFactory(DataFactory.HIDDEN);
              
                static {
                  GCCYCLEEVENTS.add("GlobalGC");
                  GCCYCLEEVENTS.add("LocalGC");
                }
              
                protected boolean isTimeStampRelative()
                {
                  return false;
                }
              
                public SourceData parse(Source source, OutputProperties properties)
                {
                  SourceData data = new SourceDataImpl(source.getName());
                  this.dataManager = new VGCDataManager(source, data, properties);
                  initialiseAxesAndKnownTuples(source, properties);
                  List events = extractEvents(source);
                  populateTuples(events);
                  addMessages(data);
                  this.dataManager.complete();
                  return data;
                }
              
                private void addMessages(SourceData data) {
                  StructuredDataBuilder info = this.hiddenFactory.createStructuredData(
                    InstanceJavaDumpParser.class.getName(), 
                    InstanceJavaDumpParser.class.getName());
                  info.put("FILETYPE", "javacore");
                  if (this.verboseGCEnabled) {
                    info.put("VERBOSEGCENABLED", "enabled");
                    if (this.verboseGCLogName != null) {
                      info.put("VERBOSEGCLOG", this.verboseGCLogName);
                    }
                  }
                  data.addData(info, DataLevel.VARIANT);
                  UnstructuredData versionData = factory.createUnstructuredData(
                    "VGCLabels.version", Messages.getString("VGCLabels.version"), "IBM");
                  data.addData(versionData, DataLevel.VARIANT);
                }
              
                private void initialiseAxesAndKnownTuples(Source source, OutputProperties properties)
                {
                  this.allFields.put("Mark", "VGCLabels.mark.times");
                  this.allFields.put("Sweep", "VGCLabels.sweep.times");
                  this.allFields.put("Compact", "VGCLabels.compact.times");
                  this.allFields.put("GlobalGC", 
                    "VGCLabels.pause.times.with.exclusive.access");
              
                  for (String gcType : new String[] { "GlobalGC", "LocalGC" }) {
                    this.allFields.put(gcType + ".end.newspace.free", 
                      "VGCLabels.free.nursery.heap");
                    this.allFields.put(gcType + ".end.newspace.used", 
                      "VGCLabels.live.nursery.heap.after.gc");
                    this.allFields.put(gcType + ".end.newspace.total", 
                      "VGCLabels.nursery.size");
                    this.allFields.put(gcType + ".end.oldspace.free", 
                      "VGCLabels.free.tenured.heap");
                    this.allFields.put(gcType + ".end.oldspace.used", 
                      "VGCLabels.live.tenured.heap.after.gc");
                    this.allFields.put(gcType + ".end.oldspace.total", 
                      "VGCLabels.tenured.heap.size");
                    this.allFields.put(gcType + ".end.space.free", 
                      "VGCLabels.free.flat.heap");
                    this.allFields.put(gcType + ".end.space.used", 
                      "VGCLabels.live.normal.heap.after.gc");
                    this.allFields
                      .put(gcType + ".end.space.total", "VGCLabels.flat.heap.size");
                    this.allFields
                      .put(gcType + ".end.loa.free", "VGCLabels.free.loa.after.collection");
                    this.allFields
                      .put(gcType + ".end.loa.used", "VGCLabels.used.loa.after.collection");
                    this.allFields.put(gcType + ".end.loa.total", 
                      "VGCLabels.total.loa.after");
              
                    this.allFields.put(gcType + ".diff.weakrefs", 
                      "VGCLabels.weak.references.cleared");
                    this.allFields.put(gcType + ".diff.soft", 
                      "VGCLabels.soft.references.cleared");
                    this.allFields.put(gcType + ".diff.phantom", 
                      "VGCLabels.phantom.references.cleared");
              
                    this.allFields.put(gcType + ".start.weakrefs", 
                      "VGCLabels.weak.references.before.collection");
                    this.allFields.put(gcType + ".start.soft", 
                      "VGCLabels.soft.references.before.collection");
                    this.allFields.put(gcType + ".start.phantom", 
                      "VGCLabels.phantom.references.before.collection");
              
                    this.allFields.put(gcType + ".end.weakrefs", 
                      "VGCLabels.weak.references.after.collection");
                    this.allFields
                      .put(gcType + ".end.soft", "VGCLabels.soft.references.after.collection");
                    this.allFields.put(gcType + ".end.phantom", 
                      "VGCLabels.phantom.references.after.collection");
                  }
                }
              
                private List<EventRecord> extractEvents(Source source) {
                  List events = new LinkedList();
                  try {
                    BufferedReader r = source.getSafeBufferedStreamReader();
                    String line = r.readLine();
                    Pattern startPattern = Pattern.compile("3STHSTTYPE     ([0-9][0-9]:[0-9][0-9]:[0-9][0-9]:[0-9][0-9][0-9]* [A-Z][A-Z][A-Z]) j9mm.[0-9][0-9] -   ([A-Za-z0-9]+) start:? (.*)");
                    Pattern endPattern = Pattern.compile("3STHSTTYPE     ([0-9][0-9]:[0-9][0-9]:[0-9][0-9]:[0-9][0-9][0-9]* [A-Z][A-Z][A-Z]) j9mm.[0-9][0-9] -   ([A-Za-z0-9]+) end:? (.*)");
                    Pattern coreTimeStampPattern = 
                      Pattern.compile("1TIDATETIME.*Date:.* ([0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]) at [0-9][0-9]:[0-9][0-9]:[0-9][0-9]");
                    Pattern verboseGCFlagPattern = 
                      Pattern.compile("2CIUSERARG *-verbose:gc");
                    Pattern verboseGCLogPattern = Pattern.compile("2CIUSERARG *-Xverbosegclog:(.*)");
                    while (line != null) {
                      Matcher startMatcher = startPattern.matcher(line);
                      Matcher endMatcher = endPattern.matcher(line);
                      Matcher coreTimeStampMatcher = coreTimeStampPattern
                        .matcher(line);
                      Matcher verboseGCFlagMatcher = verboseGCFlagPattern
                        .matcher(line);
                      Matcher verboseGCLogMatcher = verboseGCLogPattern.matcher(line);
                      if (startMatcher.matches()) {
                        String timeStamp = startMatcher.group(1);
                        String eventType = startMatcher.group(2);
                        String extraData = startMatcher.group(3);
                        events.add(new EventRecord(timeStamp, 
                          1, eventType, extraData));
                      }
                      if (endMatcher.matches()) {
                        String timeStamp = endMatcher.group(1);
                        String eventType = endMatcher.group(2);
                        String extraData = endMatcher.group(3);
                        events.add(new EventRecord(timeStamp, 2, 
                          eventType, extraData));
                      }
                      if (coreTimeStampMatcher.matches()) {
                        String coreTimeStamp = coreTimeStampMatcher.group(1);
                        try {
                          long coreTimeStampMillis = new SimpleDateFormat(
                            "yyyy/MM/dd").parse(coreTimeStamp).getTime();
              
                          this.baseTimeStamp = coreTimeStampMillis;
                        }
                        catch (ParseException e) {
                          e.printStackTrace();
                        }
                      }
                      if (verboseGCFlagMatcher.matches()) {
                        this.verboseGCEnabled = true;
                      }
                      if (verboseGCLogMatcher.matches()) {
                        this.verboseGCEnabled = true;
              
                        this.verboseGCLogName = verboseGCLogMatcher.group(1).split(",")[0];
                      }
              
                      line = r.readLine();
                    }
                  }
                  catch (GCAndMemoryVisualizerException e) {
                    e.printStackTrace();
                  }
                  catch (IOException e) {
                    e.printStackTrace();
                  }
                  return events;
                }
              
                private void populateTuples(List<EventRecord> events)
                {
                  Comparator comparator = new Comparator() {
                    public int compare(InstanceJavaDumpParser.EventRecord o1, InstanceJavaDumpParser.EventRecord o2) {
                      if (o1.sortableStamp > o2.sortableStamp)
                        return -1;
                      if (o1.sortableStamp < o2.sortableStamp) {
                        return 1;
                      }
                      return 0;
                    }
             

@Override
public int compare(Object o1, Object o2) {
	// TODO Auto-generated method stub
	return 0;
}     };
                  Collections.sort(events, Collections.reverseOrder(comparator));
              
                  HashMap eventStartValues = new HashMap();
              
                  boolean noTimeStamp = true;
              
                  for (EventRecord e : events) {
                    String eventKind = e.eventKind == 1 ? "start" : 
                      "end";
              
                    if (((e.eventKind == 1) && 
                      (GCCYCLEEVENTS.contains(e.eventType))) || 
                      (noTimeStamp)) {
                      double milliTimeStamp = e.microSecondTimeStamp() / 1000.0D;
                      milliTimeStamp += this.baseTimeStamp;
              
                      if (milliTimeStamp < this.dataManager.getCurrentX()) {
                        milliTimeStamp += 86400000.0D;
                      }
                      setCurrentTimestamp(milliTimeStamp);
                      noTimeStamp = false;
                    }
              
                    if (e.eventKind == 1) {
                      eventStartValues.put(e.eventType, Double.valueOf(e.microSecondTimeStamp()));
                      if (e.extraData != null) {
                        String eventPrefix = e.eventType + "." + eventKind + ".";
                        extracExtraStartData(e, eventPrefix);
                      }
                    } else if (e.eventKind == 2)
                    {
                      if (eventStartValues.get(e.eventType) != null)
                      {
                        double startTime = ((Double)eventStartValues.remove(e.eventType)).doubleValue();
                        double endTime = e.microSecondTimeStamp();
                        double duration = endTime - startTime;
              
                        addDataPoint(findOrCreatePauseField(e.eventType), 
                          duration / 1000.0D);
                      }
                      if (e.extraData != null)
                        extracExtraEndData(e, e.eventType);
                    }
                  }
                }
              
                private void extracExtraStartData(EventRecord e, String eventPrefix)
                {
                  for (String data : e.extraData.split(" "))
                    if (data.indexOf("=") != -1)
                    {
                      String[] dataSplit = data.split("=");
                      String dataName = eventPrefix + dataSplit[0];
                      String valueString = dataSplit[1];
                      if (valueString.indexOf("/") == -1) {
                        double value = Double.parseDouble(valueString);
                        addDataPoint(findOrCreateCountField(dataName), value);
                      } else {
                        String[] values = valueString.split("/");
                        String usedStr = values[0];
                        String totalStr = values[1];
                        double free = Double.parseDouble(usedStr);
                        double total = Double.parseDouble(totalStr);
              
                        if (total > 0.0D) {
                          addDataPoint(findOrCreateBytesField(dataName + ".free"), 
                            free);
                          addDataPoint(findOrCreateBytesField(dataName + ".total"), 
                            total);
                          addDataPoint(findOrCreateBytesField(dataName + ".used"), 
                            total - free);
                        }
                      }
                    }
                }
              
                private void extracExtraEndData(EventRecord e, String eventType)
                {
                  String eventEndPrefix = eventType + ".end.";
                  String eventStartPrefix = eventType + ".start.";
                  boolean heapSizesFound = false;
                  double newSpaceFree = 0.0D; double newSpaceTotal = 0.0D; double oldSpaceFree = 0.0D; double oldSpaceTotal = 0.0D;
                  for (String data : e.extraData.split(" "))
                  {
                    if (data.indexOf("=") != -1)
                    {
                      String[] dataSplit = data.split("=");
                      String dataName = dataSplit[0];
                      String dataEndName = eventEndPrefix + dataName;
                      String dataStartName = eventStartPrefix + dataName;
                      String valueString = dataSplit[1];
                      if (valueString.indexOf("/") == -1) {
                        double endValue = Double.parseDouble(valueString);
                        addDataPoint(findOrCreateCountField(dataEndName), endValue);
                        if (this.allFields.get(dataStartName) != null) {
                          String fieldName = (String)this.allFields.get(dataStartName);
                          if (this.dataManager.fieldHasValues(fieldName)) {
                            double startVal = this.dataManager
                              .getLastValue(fieldName);
                            double difference = startVal - endValue;
                            String dataDifferenceName = eventType + ".diff." + 
                              dataName;
                            addDataPoint(
                              findOrCreateCountField(dataDifferenceName), 
                              difference);
                          }
                        }
                      } else {
                        String[] values = valueString.split("/");
                        String usedStr = values[0];
                        String totalStr = values[1];
                        double free = Double.parseDouble(usedStr);
                        double total = Double.parseDouble(totalStr);
              
                        if (total > 0.0D) {
                          addDataPoint(findOrCreateBytesField(dataEndName + ".free"), 
                            free);
                          addDataPoint(
                            findOrCreateBytesField(dataEndName + ".total"), 
                            total);
                          addDataPoint(findOrCreateBytesField(dataEndName + ".used"), 
                            total - free);
              
                          if (dataName.equals("oldspace")) {
                            heapSizesFound = true;
                            oldSpaceFree = free;
                            oldSpaceTotal = total;
                          }
                          if (dataName.equals("newspace")) {
                            newSpaceFree = free;
                            newSpaceTotal = total;
                          }
                        }
                      }
                    }
                  }
                  if (heapSizesFound) {
                    String dataEndName = eventEndPrefix + "space";
                    double free = oldSpaceFree + newSpaceFree;
                    double total = oldSpaceTotal + newSpaceTotal;
                    addDataPoint(findOrCreateBytesField(dataEndName + ".free"), free);
                    addDataPoint(findOrCreateBytesField(dataEndName + ".total"), total);
                    addDataPoint(findOrCreateBytesField(dataEndName + ".used"), total - 
                      free);
                  }
                }
              
                private String findOrCreatePauseField(String eventType) {
                  String fieldName = (String)this.allFields.get(eventType);
                  if (fieldName == null) {
                    fieldName = eventType;
                    this.dataManager.createDynamicPauseField(fieldName);
                    this.allFields.put(eventType, fieldName);
                  }
                  return fieldName;
                }
              
                private String findOrCreateCountField(String eventType) {
                  String fieldName = (String)this.allFields.get(eventType);
                  if (fieldName == null) {
                    fieldName = eventType;
                    this.dataManager.createDynamicCountField(fieldName);
                    this.allFields.put(eventType, fieldName);
                  }
                  return fieldName;
                }
              
                private String findOrCreateBytesField(String eventType) {
                  String fieldName = (String)this.allFields.get(eventType);
                  if (fieldName == null) {
                    fieldName = eventType;
                    this.dataManager.createDynamicBytesField(fieldName);
                    this.allFields.put(eventType, fieldName);
                  }
                  return fieldName; } 
                private static class EventRecord { public static final int EVENTRECORD = 0;
                  public static final int EVENTSTART = 1;
                  public static final int EVENTEND = 2;
                  public final String timeStamp;
                  public final int eventKind;
                  public final String eventType;
                  public final String extraData;
                  protected final double sortableStamp;
              
                  public EventRecord(String timeStamp, int eventKind, String eventType, String extraData) { this.timeStamp = timeStamp;
                    this.eventKind = eventKind;
                    this.eventType = eventType;
                    this.extraData = extraData;
                    this.sortableStamp = sortableStamp();
                  }
              
                  private double sortableStamp()
                  {
                    String[] timeStampParts = this.timeStamp.split(" ")[0].split(":");
                    double sortableTimeStamp = Double.valueOf(timeStampParts[0] + 
                      timeStampParts[1] + timeStampParts[2]).doubleValue();
              
                    sortableTimeStamp += Double.valueOf(timeStampParts[3]).doubleValue() / 1000000000.0D;
                    return sortableTimeStamp;
                  }
              
                  public double microSecondTimeStamp()
                  {
                    String[] timeStampParts = this.timeStamp.split(" ")[0].split(":");
                    long hourMicros = Long.valueOf(timeStampParts[0]).longValue() * 60L * 
                      60L * 1000000L;
                    long minuteMicros = Long.valueOf(timeStampParts[1]).longValue() * 60L * 
                      1000000L;
                    long secondMicros = Long.valueOf(timeStampParts[2]).longValue() * 1000000L;
              
                    long nanoMicros = Long.valueOf(timeStampParts[3]).longValue() / 1000L;
                    double micros = hourMicros + minuteMicros + secondMicros + nanoMicros;
                    return micros;
                  }
                }
              }

